use std::io;
use std::io::Read;

use actix_files as fs;
use actix_service::Service;
use actix_web::{App, HttpResponse, HttpServer, Responder, web};
use actix_web::http::{HeaderName, HeaderValue};
use futures::future::Future;

use info::Info;

use crate::info::Config;

use super::*;

lazy_static! {
    pub static ref CONFIG: Config = {
        let mut file = std::fs::File::open("config.toml").expect("config.toml not found!");
        let mut config_str = String::from("");
        file.read_to_string(&mut config_str)
            .expect("read config.toml file failed!");
        toml::from_str(&config_str).expect("convert config.toml properties failed!")
    };
}

fn jpg(info: web::Path<Info>) -> impl Responder {
    let path = get_path(&info.tiles);
    let tile_data = select_tile_data(&path.0, &path.1, info.z, info.x, info.y);
    HttpResponse::Ok()
        .header("Access-Control-Allow-Origin", "*")
        .content_type("image/jpeg")
        .body(tile_data)
}

fn png(info: web::Path<Info>) -> impl Responder {
    let path = get_path(&info.tiles);
    let tile_data = select_tile_data(&path.0, &path.1, info.z, info.x, info.y);
    HttpResponse::Ok()
        .header("Access-Control-Allow-Origin", "*")
        .content_type("image/png")
        .body(tile_data)
}

fn mark(info: web::Path<Info>) -> impl Responder {
    let path = get_path(&info.tiles);
    let tile_data = select_tile_data_mark(&path.0, &path.1, info.z, info.x, info.y);
    HttpResponse::Ok()
        .header("Access-Control-Allow-Origin", "*")
        .content_type("image/png")
        .body(tile_data)
}

fn mvt(info: web::Path<Info>) -> impl Responder {
    let path = get_path(&info.tiles);
    let tile_data = select_tile_data(&path.0, &path.1, info.z, info.x, info.y);
    HttpResponse::Ok()
        .content_type("application/vnd.mapbox-vector-tile")
        .header("Access-Control-Allow-Origin", "*")
        .header("content-encoding", "gzip")
        .body(tile_data)
}

fn get_path(tiles: &String) -> (String, String) {
    let config_tiles = &CONFIG.tiles;
    let x1 = config_tiles
        .into_iter()
        .filter(|e| &e.name == tiles)
        .map(|ee| (ee.name.to_owned(), ee.path.to_owned()))
        .collect::<Vec<(String, String)>>();
    let default = String::from("");
    let default2 = String::from("");
    let path = x1.get(0).unwrap_or(&(default, default2)).to_owned();
    return path;
}

/// http server的启动方法
/// `addr` 是http服务要绑定的ip和端口
pub fn start(addr: &str) -> io::Result<()> {
    HttpServer::new(|| {
        let mut app = App::new()
            .wrap_fn(|req, srv| {
                srv.call(req).map(|mut res| {
                    res.headers_mut().insert(
                        HeaderName::from_static("access-control-allow-origin"),
                        HeaderValue::from_static("*"),
                    );
                    res
                })
            })
            .route(r"/mbtiles/{tiles}/{z}/{x}/{y}.jpg", web::get().to(jpg))
            .route(r"/mbtiles/{tiles}/{z}/{x}/{y}.png", web::get().to(png))
            .route(r"/mark/{tiles}/{z}/{x}/{y}.png", web::get().to(mark))
            .route(r"/mbtiles/{tiles}/{z}/{x}/{y}.pbf", web::get().to(mvt))
            .service(fs::Files::new("/static", "./static/").show_files_listing())
            .service(fs::Files::new("/examples", "./examples/").show_files_listing());

        let config_tiles = &CONFIG.tiles;
        let directories = config_tiles
            .into_iter()
            .filter(|e| e.ttype == "raster-directory")
            .collect::<Vec<&info::Tiles>>();
        // 目录静态地址
        for x in directories {
            app = app.service(
                fs::Files::new(format!("/tiles/{}", x.name).as_str(), x.path.as_str())
                    .show_files_listing(),
            );
        }
        app
    })
        .bind(addr)?
        .run()?;
    Ok(())
}
